#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/fb.h>//开发板显示屏配置头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include"taiji.h"


/**
 * 这个结构体用于描述帧缓冲设备的屏幕信息,包括:
 *xres/yres - 屏幕的水平和垂直分辨率
 *bits_per_pixel - 每个像素的位数
 *grayscale - 是否是灰度屏
 *red/green/blue - 红绿蓝色彩亮度范围
 *transp - 透明度值
 *rotate - 屏幕旋转角度
 */
struct fb_var_screeninfo lcd_info;

int lcd_fd=-1;
int *lcd_fp=NULL;

// 后缓冲区和当前绘图目标
int *back_buffer = NULL;
int *current_buffer = NULL;
int buffer_initialized = 0;


//初始化屏幕
void lcd_init()
{
    //打开屏幕配置文件
    lcd_fd=open("/dev/fb0",O_RDWR);
    if(lcd_fd == -1 )
    {
        perror("open lcd failed");
        return ;
    }
    /**
     *  lcd_fd: 这是打开帧缓冲设备文件后返回的文件描述符。
     *  FBIOGET_VSCREENINFO: 这是一个请求码,表示要获取变量屏幕信息
     *  &lcd_info: 这是struct fb_var_screeninfo类型的指针,用于返回获取到的屏幕信息。
    */
    ioctl(lcd_fd,FBIOGET_VSCREENINFO,&lcd_info);

    //内存映射
    //计算所需要的内存大小，横纵像素数乘每个像素所占的位数除以8，1字节占8bits
    int men_size=lcd_info.xres * lcd_info.yres * lcd_info.bits_per_pixel / 8;
    //对屏幕进行内存映射,lcd_fp为内存映射首地址
    lcd_fp=mmap(NULL,men_size,PROT_WRITE|PROT_READ,MAP_SHARED,lcd_fd,0);
    //如果对其他进程不可见，则内存映射失败
    if(lcd_fp == MAP_FAILED)
    {
        perror("mmap failed");
        close(lcd_fd);
        exit(0);
    }
    
    // 初始化后缓冲区（仅用于重玩刷新页面）
    back_buffer_init();
    
    // 默认直接操作屏幕，不使用双缓冲
    current_buffer = NULL;
}

// 初始化后缓冲区
void back_buffer_init()
{
    // 计算所需内存大小
    int mem_size = lcd_info.xres * lcd_info.yres * sizeof(int);
    
    // 分配后缓冲区内存
    back_buffer = (int *)malloc(mem_size);
    if (back_buffer == NULL)
    {
        perror("malloc back_buffer failed");
        return;
    }
    
    // 初始化后缓冲区为白色
    memset(back_buffer, 0xFF, mem_size);
    
    buffer_initialized = 1;
}

// 释放后缓冲区
void back_buffer_free()
{
    if (back_buffer != NULL)
    {
        free(back_buffer);
        back_buffer = NULL;
        buffer_initialized = 0;
    }
}

// 交换前后缓冲区（一次性将后缓冲区内容复制到屏幕）
void swap_buffer()
{
    if (!buffer_initialized || back_buffer == NULL)
    {
        return;
    }
    
    // 计算屏幕总像素数
    int total_pixels = lcd_info.xres * lcd_info.yres;
    
    // 一次性将后缓冲区内容复制到屏幕缓冲区
    memcpy(lcd_fp, back_buffer, total_pixels * sizeof(int));
}


//显示单个像素点（默认直接操作屏幕）
void draw_pixel(int x,int y,int color)
{
    if(x<lcd_info.xres && x>=0 && y<lcd_info.yres && y>=0 && lcd_fp != NULL)
    {
        *(lcd_fp + y*lcd_info.xres + x) = color;
    }
}

// 用于双缓冲绘制的像素点函数（内部使用）
void draw_pixel_buffer(int x, int y, int color)
{
    if(x<lcd_info.xres && x>=0 && y<lcd_info.yres && y>=0 && back_buffer != NULL)
    {
        *(back_buffer + y*lcd_info.xres + x) = color;
    }
}


//设置全屏背景颜色（默认直接操作屏幕）
void draw_background(int color)
{
    // 直接操作屏幕
    for(int y=0; y<lcd_info.yres; y++)
    {
        for(int x=0; x<lcd_info.xres; x++)
        {
            draw_pixel(x, y, color);
        }
    }
}

// 准备双缓冲（用于重玩刷新页面）
void prepare_double_buffer()
{
    if (!buffer_initialized || back_buffer == NULL)
    {
        return;
    }
    
    // 清空后缓冲区为白色
    memset(back_buffer, 0xFF, lcd_info.xres * lcd_info.yres * sizeof(int));
}

// 在后缓冲区绘制背景（用于重玩刷新页面）
void draw_background_buffer(int color)
{
    if (!buffer_initialized || back_buffer == NULL)
    {
        return;
    }
    
    if (color == 0xFFFFFF)
    {
        memset(back_buffer, 0xFF, lcd_info.xres * lcd_info.yres * sizeof(int));
    }
    else
    {
        for(int y=0; y<lcd_info.yres; y++)
        {
            for(int x=0; x<lcd_info.xres; x++)
            {
                draw_pixel_buffer(x, y, color);
            }
        }
    }
}

// 在后缓冲区绘制像素点（用于重玩刷新页面）
void draw_picture_buffer(int posx, int posy, const char* pic_path)
{
    if (!buffer_initialized || back_buffer == NULL)
    {
        return;
    }
    
    // 保存当前lcd_fp，临时替换为back_buffer
    int *temp_lcd_fp = lcd_fp;
    lcd_fp = back_buffer;
    
    // 调用原始的draw_picture函数
    draw_picture(posx, posy, pic_path);
    
    // 恢复原始lcd_fp
    lcd_fp = temp_lcd_fp;
}

// 在后缓冲区绘制棋子（用于重玩刷新页面）
void draw_round_buffer(int x, int y, int n, int color)
{
    if (!buffer_initialized || back_buffer == NULL)
    {
        return;
    }
    
    // 保存当前lcd_fp，临时替换为back_buffer
    int *temp_lcd_fp = lcd_fp;
    lcd_fp = back_buffer;
    
    // 绘制完整的圆形
    draw_round1(x, y, n, color);
    draw_round2(x, y, n, color);
    
    // 恢复原始lcd_fp
    lcd_fp = temp_lcd_fp;
}

//画左半圆,x、y为圆心（直接操作屏幕）
void draw_round1(int x,int y,int n,int color)
{
    int max_x = x;
    int max_y = lcd_info.yres;
    
    for(int i=0; i<=max_x; i++)
    {
        for(int j=0; j<max_y; j++)
        {
            int dx = x - i;
            int dy = y - j;
            if(dx*dx + dy*dy <= n*n)
            {
                draw_pixel(i, j, color);
            }
        }
    }
}

//画右半圆,x、y为圆心（直接操作屏幕）
void draw_round2(int x,int y,int n,int color)
{
    int max_x = lcd_info.xres;
    int max_y = lcd_info.yres;
    
    for(int i=x; i<max_x; i++)
    {
        for(int j=0; j<max_y; j++)
        {
            int dx = x - i;
            int dy = y - j;
            if(dx*dx + dy*dy <= n*n)
            {
                draw_pixel(i, j, color);
            }
        }
    }
}

//画图片到当前缓冲区
void draw_picture(int posx,int posy,const char* pic_path)
{
    //打开图片
    int bmpfd=open(pic_path,O_RDONLY);
    if(bmpfd==-1)
    {
        perror("open bmp fail\n");
        exit(0);
    }
    int width;
    int height;
    lseek(bmpfd,0x12,SEEK_SET);//光标定位到宽度位置
	//读取高度和宽度
    read(bmpfd,&width,4);
	read(bmpfd,&height,4);
	lseek(bmpfd,54,SEEK_SET);
    //定位光标到像素数组位置
	char buf[width*height*3];
	read(bmpfd,buf,width*height*3);
    //读取各个像素点保存在 buf中
	int i,j;
	int color;
	int n=0;
	unsigned char r,g,b;
	
	// 优化：直接写入缓冲区而不是通过draw_pixel函数（提高效率）
    int *target_buffer = (current_buffer != NULL) ? current_buffer : lcd_fp;
    if (target_buffer != NULL)
    {
        for(i=height-1;i>=0;i--)
        {
            for(j=0;j<width;j++)
            {
                b = buf[n++];
                g = buf[n++];
                r = buf[n++];
                color = r << 16 | g << 8 | b;
                
                int pixel_x = j + posx;
                int pixel_y = i + posy;
                
                // 检查边界
                if(pixel_x < lcd_info.xres && pixel_x >= 0 && pixel_y < lcd_info.yres && pixel_y >= 0)
                {
                    *(target_buffer + pixel_y * lcd_info.xres + pixel_x) = color;
                }
            }
        }
    }
    else
    {
        // 兼容旧方式
        for(i=height-1;i>=0;i--)
        {
            for(j=0;j<width;j++)
            {
                b = buf[n++];
                g = buf[n++];
                r = buf[n++];
                color = r << 16 | g << 8 | b;
                draw_pixel(j+posx,i+posy,color);
            }
        }
    }
    close(bmpfd);
}

//画棋盘
void checkerboard()
{
    //竖向
    for(int i=10;i<480;i=i+30)
    {
        for(int j=10;j<460;j++)
        {
            draw_pixel(i,j,0x00000000);
        }
    }
    //横向
    for(int i=10;i<460;i++)
    {
        for(int j=10;j<480;j=j+30)
        {
            draw_pixel(i,j,0x00000000);
        }
    }
}

